| 标  志 寄存器  | 
BIT31—BIT18 | BIT17 | BIT16 | BIT15 | BIT14 | BIT13—BIT12 | BIT11 | BIT10 | BIT9 | BIT8 | BIT7 | BIT6 | BIT5 | BIT4 | BIT3 | BIT2 | BIT1 | BIT0 | 
| 00000000000000 | V M  | 
R F  | 
0 | N T  | 
IOPL | OF | D F  | 
I F  | 
T F  | 
S F  | 
Z F  | 
0 | A F  | 
0 | P F  | 
1 | C F  | 
| I/O敏感指令 | 指令 | 功能 | 保护方式下的执行条件 | 
| CLI | 清除EFLAGS中的IF位 | CPL<=IOPL | |
| STI | 设置EFLAGS中的IF位 | CPL<=IOPL | |
| IN | 从I/O地址读出数据 | CPL<=IOPL或I/O位图许可 | |
| INS | 从I/O地址读出字符串 | CPL<=IOPL或I/O位图许可 | |
| OUT | 向I/O地址写数据 | CPL<=IOPL或I/O位图许可 | |
| OUTS | 向I/O地址写字符串 | CPL<=IOPL或I/O位图许可 | 
TSSSEG                  SEGMENT PARA USE16
                        TSS     <>             ;TSS低端固定格式部分
                        DB      8 DUP(0)       ;对应I/O端口00H—3FH
                        DB      10000000B      ;对应I/O端口40H—47H
                        DB      01100000B      ;对用I/O端口48H—4FH
                        DB      8182 DUP(0ffH) ;对应I/O端口50H—0FFFFH
                        DB      0FFH           ;位图结束字节
TSSLen                  =       $
TSSSEG                  ENDS
                        in      al,21h  ;(1)正常执行
                        in      al,47h  ;(2)引起异常
                        out     20h,al  ;(3)正常实行
                        out     4eh,al  ;(4)引起异常
                        in      al,20h  ;(5)正常执行
                        out     20h,eax ;(6)正常执行
                        out     4ch,ax  ;(7)引起异常
                        in      ax,46h  ;(8)引起异常
                        in      eax,42h ;(9)正常执行
| 
不同特权级对 标志寄存器特 殊字段的处理  | 
特权级 | VM标志字段 | IOPL标志字段 | IF标志字段 | 
| CPL=0 | 可变(初POPF指令外) | 可变 | 可变 | |
0| 不变 | 
不变 | 
可变 | 
 | |
| CPL>IOPL | 不变 | 不变 | 不变 | 
;名称:ASM9.ASM
;功能:演示I/O保护及I/O敏感指令的作用
;编译:TASM ASM9.ASM
;连接:TLINK /32 ASM9.OBJ
;----------------------------------------------------------------------------
INCLUDE         386SCD.INC
;----------------------------------------------------------------------------
GDTSeg          SEGMENT PARA USE16                ;全局描述符表段(16位)
GDT             LABEL   BYTE
                ;空描述符
DUMMY           Desc    <>
                ;规范段描述符及选择子
Normal          Desc    <0ffffh,,,ATDW,,>
Normal_Sel      =       Normal-GDT
                ;视频缓冲区段描述符(DPL=3)及选择子(任何特权级可写)
VideoBuf        Desc    <07fffh,8000h,0bh,ATDW,,>
VideoBuf_Sel    =       VideoBuf-GDT
;----------------------------------------------------------------------------
EFFGDT          LABEL   BYTE
                ;演示任务TSS段描述符及选择子
DemoTSS         Desc    <DemoTSSLen-1,DemoTSSSeg,,AT386TSS,,>
DemoTSS_Sel     =       DemoTSS-GDT
                ;演示任务堆栈段描述符及选择子
DemoStack       Desc    <DemoStackLen-1,DemoStackSeg,,ATDW,D32,>
DemoStack_Sel   =       DemoStack-GDT
                ;演示代码段描述符及选择子
DemoCode        Desc    <DemoCodeLen-1,DemoCodeSeg,,ATCE,D32,>
DemoCode_Sel    =       DemoCode-GDT
                ;属于演示任务的临时代码段描述符及选择子
TempCode        Desc    <0ffffh,TempCodeSeg,,ATCE,,>
TempCode_Sel    =       TempCode-GDT
                ;指向GDT的存储段描述符及选择子
ToGDT           Desc    <GDTLen-1,GDTSeg,,ATDW,,>
ToGDT_Sel       =       ToGDT-GDT
                ;指向通用保护故障处理任务TSS的存储段描述符及选择子
ToGPTSS         Desc    <GPTSSLen-1,GPTSSSeg,,ATDW,,>
ToGPTSS_Sel     =       ToGPTSS-GDT
                ;指向测试任务TSS的存储段描述符及选择子
ToTestTSS       Desc    <TestTSSLen-1,TestTSSSeg,,ATDW,,>
ToTestTSS_Sel   =       ToTestTSS-GDT
                ;测试任务TSS段描述符及选择子
TestTSS         Desc    <TestTSSLen-1,TestTSSSeg,,AT386TSS,,>
TestTSS_Sel     =       TestTSS-GDT
                ;测试任务1堆栈段描述符(DPL=1)及选择子
Test1Stack      Desc    <TestStackLen-1,TestStackSeg,,ATDW+DPL1,D32,>
Test1Stack_Sel  =       Test1Stack-GDT+RPL1
                ;测试任务1代码段描述符(DPL=1)及选择子
Test1Code       Desc    <TestCodeLen-1,TestCodeSeg,,ATCE+DPL1,D32,>
Test1Code_Sel   =       Test1Code-GDT+RPL1
                ;测试任务2堆栈段描述符(DPL=2)及选择子
Test2Stack      Desc    <TestStackLen-1,TestStackSeg,,ATDW+DPL2,D32,>
Test2Stack_Sel  =       Test2Stack-GDT+RPL2
                ;测试任务2代码段描述符(DPL=2)及选择子
Test2Code       Desc    <TestCodeLen-1,TestCodeSeg,,ATCE+DPL2,D32,>
Test2Code_Sel   =       Test2Code-GDT+RPL2
                ;测试任务3堆栈段描述符(DPL=3)及选择子
Test3Stack      Desc    <TestStackLen-1,TestStackSeg,,ATDW+DPL3,D32,>
Test3Stack_Sel  =       Test3Stack-GDT+RPL3
                ;测试任务3代码段描述符(DPL=3)及选择子
Test3Code       Desc    <TestCodeLen-1,TestCodeSeg,,ATCE+DPL3,D32,>
Test3Code_Sel   =       Test3Code-GDT+RPL3
                ;通用保护故障处理任务的TSS段描述符及选择子
GPTSS           Desc    <GPTSSLen-1,GPTSSSeg,,AT386TSS,,>
GPTSS_Sel       =       GPTSS-GDT
                ;通用保护故障处理任务的堆栈段描述符及选择子
GPStack         Desc    <GPStackLen-1,GPStackSeg,,ATDW,D32,>
GPStack_Sel     =       GPStack-GDT
                ;通用保护故障处理任务的代码段描述符及选择子
GPCode          Desc    <GPCodeLen-1,GPCodeSeg,,ATCE,D32,>
GPCode_Sel      =       GPCode-GDT
                ;其它中断或异常处理程序代码段(一致可读)描述符及选择子
ErrCode         Desc    <ErrCodeLen-1,ErrCodeSeg,,ATCCOR,D32,>
ErrCode_Sel     =       ErrCode-GDT
;----------------------------------------------------------------------------
GDNum           =       ($-EFFGDT)/(SIZE Desc)    ;需处理基地址的描述符个数
;----------------------------------------------------------------------------
                ;指向测试任务的任务门
TestTask        Gate    <,TestTSS_Sel,,ATTaskGate,>
Test_Sel        =       TestTask-GDT
;----------------------------------------------------------------------------
GDTLen          =       $-GDT                     ;全局描述符表长度
GDTSeg          ENDS                              ;全局描述符表段定义结束
;----------------------------------------------------------------------------
IDTSeg          SEGMENT PARA USE16                ;中断描述符表段(16位)
IDT             LABEL   BYTE                      ;中断描述符表
                REPT    13
                Gate    <ErrBegin,ErrCode_Sel,,AT386TGate,>
                ENDM
                Gate    <,GPTSS_Sel,,ATTaskGate,> ;通用故障处理程序门描述符
                REPT    242
                Gate    <ErrBegin,ErrCode_Sel,,AT386TGate,>
                ENDM
;----------------------------------------------------------------------------
IDTLen          =       $-IDT
;----------------------------------------------------------------------------
IDTSeg          ENDS                              ;中断描述符表段定义结束
;----------------------------------------------------------------------------
;其它中断或异常处理程序的代码段(一致可读)
;----------------------------------------------------------------------------
ErrCodeSeg      SEGMENT PARA USE32
                ASSUME  CS:ErrCodeSeg
;----------------------------------------------------------------------------
ErrMess         DB      'Error!!!'
ErrMessLen      =       $-ErrMess
;----------------------------------------------------------------------------
ErrBegin        PROC    FAR
                cld
                mov     ax,ErrCode_Sel
                mov     ds,ax
                lea     esi,ErrMess
                mov     ax,VideoBuf_Sel
                mov     es,ax
                mov     edi,1992
                mov     ecx,ErrMessLen
                mov     ah,4eh
Err1:           lodsb
                stosw
                loop    Err1
                jmp     $
ErrBegin        ENDP
;----------------------------------------------------------------------------
ErrCodeLen      =       $
ErrCodeSeg      ENDS
;----------------------------------------------------------------------------
GPTSSSeg        SEGMENT PARA USE16                ;通用保护故障处理任务的TSS
GPTaskSS        LABEL   BYTE
                DD      0                         ;任务嵌套时的链接指针
                DD      0                         ;0级堆栈偏移
                DW      0,0                       ;0级堆栈选择子
                DD      0                         ;1级堆栈偏移
                DW      0,0                       ;1级堆栈选择子
                DD      0                         ;2级堆栈偏移
                DW      0,0                       ;2级堆栈选择子
                DD      0                         ;CR3
                DW      GPBegin,0                 ;EIP
                DD      0                         ;EFLAGS
                DD      0                         ;EAX
                DD      0                         ;ECX
                DD      0                         ;EDX
                DD      0                         ;EBX
                DD      GPStackLen                ;ESP
                DD      0                         ;EBP
                DD      0                         ;ESI
                DD      0                         ;EDI
                DW      VideoBuf_Sel,0            ;ES
                DW      GPCode_Sel,0              ;CS
                DW      GPStack_Sel,0             ;SS
                DW      ToTestTSS_Sel,0           ;DS
                DW      ToGPTSS_Sel,0             ;FS
                DW      0,0                       ;GS
                DW      0,0                       ;LDTR
                DW      0                         ;调试陷阱标志
                DW      $+2                       ;指向I/O许可位图的偏移
                DB      0ffh                      ;I/O许可位图结束标志
GPTSSLen        =       $
GPTSSSeg        ENDS
;----------------------------------------------------------------------------
GPStackSeg      SEGMENT PARA USE32                ;通用保护故障处理任务堆栈段
GPStackLen      =       512
                DB      GPStackLen DUP(0)
GPStackSeg      ENDS
;----------------------------------------------------------------------------
;通用保护故障处理程序代码段
;----------------------------------------------------------------------------
GPCodeSeg       SEGMENT PARA USE32
                ASSUME  CS:GPCodeSeg
;----------------------------------------------------------------------------
GPBegin         PROC    FAR
                ;在屏幕左上角显示故障点
                xor     edi,edi
                mov     ebx,OFFSET TestTaskSS
                mov     edx,DWORD PTR [ebx].TRCS
                call    EchoEDX
                mov     ax,(17h SHL 8)+':'
                stosw
                mov     edx,[ebx].TREIP
                call    EchoEDX
                ;演示以便看清故障点
                mov     ecx,1234567h
                loop    $
                ;调整任务链接指针,中止故障任务
                mov     ebx,OFFSET GPTaskSS
                mov     ax,DemoTSS_Sel
                mov     fs:[ebx].TRLink,ax
                add     esp,4
                iretd
                jmp     GPBegin
GPBegin         ENDP
;----------------------------------------------------------------------------
;显示edx内容的子程序
;----------------------------------------------------------------------------
EchoEDX         PROC
                mov     ah,17h
                mov     ecx,8
EchoEDX1:       rol     edx,4
                mov     al,dl
                call    HToASCII
                stosw
                loop    EchoEDX1
                ret
EchoEDX         ENDP
;----------------------------------------------------------------------------
;把4位二进制数转换成对应的ASCII码
;----------------------------------------------------------------------------
HToASCII        PROC
                and     al,0fh
                add     al,90h
                daa
                adc     al,40h
                daa
                ret
HToASCII        ENDP
;----------------------------------------------------------------------------
GPCodeLen       =       $
GPCodeSeg       ENDS
;----------------------------------------------------------------------------
;测试任务的TSS段
TestTSSSeg      SEGMENT PARA USE16
TestTaskSS      TSS     <>                        ;TSS的固定格式部分
IOMap           LABEL   BYTE                      ;I/O许可位图
                DB      8 DUP(0ffh)               ;端口00h--3fh
                DB      11111011b                 ;端口40h--47h
                DB      3 DUP(0ffh)               ;端口48h--5fh
                DB      11111101b                 ;端口60h--67h
                DB      0                         ;端口68h--6fh
                DB      0ffh                      ;I/O许可位图结束标志
TestTSSLen      =       $
TestTSSSeg      ENDS
;----------------------------------------------------------------------------
;测试任务的堆栈段
TestStackSeg    SEGMENT PARA USE32
TestStackLen    =       1024
                DB      TestStackLen DUP(0)
TestStackSeg    ENDS
;----------------------------------------------------------------------------
;测试任务的代码段
TestCodeSeg     SEGMENT PARA USE32
                ASSUME  CS:TestCodeSeg
;----------------------------------------------------------------------------
Test3Begin      PROC    FAR
                cli                               ;I/O敏感指令
                clts                              ;特权指令
                iretd
                jmp     Test3Begin
Test3Begin      ENDP
;----------------------------------------------------------------------------
TestBegin       PROC    FAR
                mov     al,0b6h                   ;使扬声器发出一长声
                out     43h,al
                mov     al,2
                out     42h,al
                mov     al,34h
                out     42h,al
                in      al,61h
                mov     ah,al
                or      al,3
                out     61h,al
                mov     ecx,1234567h
                loop    $
                mov     al,ah
                out     61h,al
                iretd
                jmp     TestBegin
TestBegin       ENDP
;----------------------------------------------------------------------------
TestCodeLen     =       $
TestCodeSeg     ENDS
;----------------------------------------------------------------------------
;演示任务TSS段
DemoTSSSeg      SEGMENT PARA USE16
DemoTaskSS      TSS     <>
                DB      0ffh                      ;I/O许可位图结束字节
DemoTSSLen      =       $
DemoTSSSeg      ENDS
;----------------------------------------------------------------------------
;演示任务的堆栈段
DemoStackSeg    SEGMENT PARA USE32
DemoStackLen    =       1024
                DB      DemoStackLen DUP(0)
DemoStackSeg    ENDS
;----------------------------------------------------------------------------
;演示任务的代码段
DemoCodeSeg     SEGMENT PARA USE32
                ASSUME  CS:DemoCodeSeg
;----------------------------------------------------------------------------
DemoBegin       PROC    FAR
                mov     ax,ToTestTSS_Sel
                mov     ds,ax
                mov     ebx,OFFSET TestTaskSS
                ;把测试任务1的入口点,堆栈指针和标志值(含IOPL)填入测试任务TSS
                mov     WORD PTR [ebx].TRSS,Test1Stack_Sel
                mov     DWORD PTR [ebx].TRESP,TestStackLen
                mov     WORD PTR [ebx].TRCS,Test1Code_Sel
                mov     DWORD PTR [ebx].TREIP,OFFSET TestBegin
                mov     DWORD PTR [ebx].TREFLAG,IOPL1
                ;通过任务门调用测试任务
                CALL32  Test_Sel,0
                ;把测试任务2的入口点,堆栈指针和标志值(含IOPL)填入测试任务TSS
                mov     WORD PTR [ebx].TRSS,Test2Stack_Sel
                mov     DWORD PTR [ebx].TRESP,TestStackLen
                mov     WORD PTR [ebx].TRCS,Test2Code_Sel
                mov     DWORD PTR [ebx].TREIP,OFFSET TestBegin
                mov     DWORD PTR [ebx].TREFLAG,IOPL1
                ;通过任务门调用测试任务
                CALL32  Test_Sel,0
                ;把测试任务TSS描述符内的属性置为"可用"
                mov     ax,ToGDT_Sel
                mov     fs,ax
                mov     fs:TestTSS.Attributes,AT386TSS
                ;把测试任务3的入口点,堆栈指针和标志值(含IOPL)填入测试任务TSS
                mov     WORD PTR [ebx].TRSS,Test3Stack_Sel
                mov     DWORD PTR [ebx].TRESP,TestStackLen
                mov     WORD PTR [ebx].TRCS,Test3Code_Sel
                mov     DWORD PTR [ebx].TREIP,OFFSET Test3Begin
                mov     DWORD PTR [ebx].TREFLAG,IOPL2
                ;通过任务门调用测试任务
                CALL32  Test_Sel,0
                ;把测试任务TSS描述符内的属性置为"可用"
                mov     ax,ToGDT_Sel
                mov     fs,ax
                mov     fs:TestTSS.Attributes,AT386TSS
                ;把测试任务4的入口点,堆栈指针和标志值(含IOPL)填入测试任务TSS
                mov     WORD PTR [ebx].TRSS,Test3Stack_Sel
                mov     DWORD PTR [ebx].TRESP,TestStackLen
                mov     WORD PTR [ebx].TRCS,Test3Code_Sel
                mov     DWORD PTR [ebx].TREIP,OFFSET Test3Begin
                mov     DWORD PTR [ebx].TREFLAG,IOPL3
                ;通过任务门调用测试任务
                CALL32  Test_Sel,0
                JUMP32  TempCode_Sel,<OFFSET ToDOS>
DemoBegin       ENDP
;----------------------------------------------------------------------------
DemoCodeLen     =       $
DemoCodeSeg     ENDS
;----------------------------------------------------------------------------
TempCodeSeg     SEGMENT PARA USE16                ;演示任务的临时代码段
                ASSUME  CS:TempCodeSeg
;----------------------------------------------------------------------------
Virtual         PROC    FAR
                ;置数据段寄存器为空
                mov     ax,0
                mov     ds,ax
                mov     es,ax
                mov     fs,ax
                mov     gs,ax
                ;置堆栈指针
                mov     ax,DemoStack_Sel
                mov     ss,ax
                mov     esp,DemoStackLen
                ;置任务寄存器
                mov     ax,DemoTSS_Sel
                ltr     ax
                ;转演示代码段
                JUMP16  DemoCode_Sel,DemoBegin
ToDOS:          clts
                mov     ax,Normal_Sel
                mov     ds,ax
                mov     es,ax
                mov     fs,ax
                mov     gs,ax
                mov     ss,ax
                mov     eax,cr0
                and     al,11111110b
                mov     cr0,eax
                JUMP16  <SEG Real>,<OFFSET Real>
Virtual         ENDP
;----------------------------------------------------------------------------
TempCodeSeg     ENDS
;============================================================================
RDataSeg        SEGMENT PARA USE16                ;实方式数据段
VGDTR           PDesc   <GDTLen-1,>               ;GDT伪描述符
VIDTR           PDesc   <IDTLen-1,>               ;IDT伪描述符
NORVIDTR        PDesc   <3ffh,>                   ;用于保存原IDTR值
SPVar           DW      ?                         ;用于保存实方式下的SP
SSVar           DW      ?                         ;用于保存实方式下的SS
RDataSeg        ENDS
;----------------------------------------------------------------------------
RCodeSeg        SEGMENT PARA USE16                ;实方式代码段
                ASSUME  CS:RCodeSeg,DS:RDataSeg
;----------------------------------------------------------------------------
Start           PROC
                mov     ax,RDataSeg
                mov     ds,ax
                cld
                call    InitGDT                   ;初始化全局描述符表GDT
                call    InitIDT                   ;初始化中断描述符表IDT
                lgdt    QWORD PTR VGDTR           ;装载GDTR
                mov     SSVar,ss                  ;保存堆栈指针
                mov     SPVar,sp
                sidt    QWORD PTR NORVIDTR        ;保存IDTR
                cli                               ;关中断
                lidt    QWORD PTR VIDTR           ;装载IDTR
                mov     eax,cr0
                or      al,1
                mov     cr0,eax
                JUMP16  <TempCode_Sel>,<OFFSET Virtual>
Real:           mov     ax,RDataSeg
                mov     ds,ax
                lss     sp,DWORD PTR SPVar        ;又回到实方式
                lidt    QWORD PTR NORVIDTR
                sti
                mov     ax,4c00h
                int     21h
Start           ENDP
;----------------------------------------------------------------------------
InitGDT         PROC
                push    ds
                mov     ax,GDTSeg
                mov     ds,ax
                mov     cx,GDNum
                mov     si,OFFSET EFFGDT
InitG:          mov     ax,[si].BaseL
                movzx   eax,ax
                shl     eax,4
                shld    edx,eax,16
                mov     WORD PTR [si].BaseL,ax
                mov     BYTE PTR [si].BaseM,dl
                mov     BYTE PTR [si].BaseH,dh
                add     si,SIZE Desc
                loop    InitG
                pop     ds
                mov     bx,16
                mov     ax,GDTSeg
                mul     bx
                mov     WORD PTR VGDTR.Base,ax
                mov     WORD PTR VGDTR.Base+2,dx
                ret
InitGDT         ENDP
;----------------------------------------------------------------------------
InitIDT         PROC
                mov     bx,16
                mov     ax,IDTSeg
                mul     bx
                mov     WORD PTR VIDTR.Base,ax
                mov     WORD PTR VIDTR.Base+2,dx
                ret
InitIDT         ENDP
;----------------------------------------------------------------------------
RCodeSeg        ENDS
                END     Start
| 参考资料 | 书 名 | 出 版 社 | 作 者 | 
| 《保护方式下的80386及其编程》 | 清华大学出版社 | 周明德主编 | |
| 《80X86汇编语言程序设计教程》 | 清华大学出版社 | 扬季文主编 |